package com.csw.android.player.view.controller

import android.animation.Animator
import android.animation.ObjectAnimator
import android.annotation.SuppressLint
import android.content.Context
import android.graphics.Color
import android.graphics.Matrix
import android.util.AttributeSet
import android.view.*
import android.widget.FrameLayout
import android.widget.TextView
import androidx.core.view.NestedScrollingChildHelper
import androidx.core.view.ViewCompat
import androidx.core.view.isGone
import androidx.core.view.isVisible
import com.csw.android.dev_utils.ex.ViewEx.gone
import com.csw.android.dev_utils.ex.ViewEx.visible
import com.csw.android.dev_utils.utils.BrightnessManager
import com.csw.android.dev_utils.utils.MediaVolumeManager
import com.csw.android.player.R
import com.csw.android.player.base.IMediaPlayer
import java.lang.Integer.max
import kotlin.math.abs
import kotlin.math.min

/**
 * 手势处理视图，本类负责手势控制处理，不同场景可以控制启用不同手势，例如短视频列表中只启用横向滑动切换进度。
 *
 * 本类对快速滑动不做处理直接对外分发、对多余的滚动量也对外分发了，可以适应在RecyclerView、ViewPager等组件的嵌套滑动处
 * 理。
 *
 * 0. 手势总控[gestureEnable]
 * 1. 单击、双击
 * 2. 横向切换进度（单指横向）[horizontalGestureEnable]
 * 3. 纵向（单指） 左边切换屏幕亮度、右侧切换系统音量[verticalGestureEnable]
 * 4. 渲染变换:画面平移（单指长按后拖动）、缩放(双指拖动)[transformGestureEnable]
 */
class GestureView @JvmOverloads constructor(
    context: Context, attrs: AttributeSet? = null
) : FrameLayout(context, attrs) {

    private enum class TouchHandler {
        SCROLL,//滚动模式，手势处理水平滚动或者，左右两侧区域的垂直滚动。
        TRANSFORM,//变换模式，手势处理缩放位移，应用于画面调整。
    }

    /** 启用手势，enable属性同此作用 */
    var gestureEnable = true

    /** 启用变换手势 */
    var transformGestureEnable = true

    /** 启用水平手势处理 */
    var horizontalGestureEnable = true

    /** 启用垂直手势处理 */
    var verticalGestureEnable = true

    /** 当前用于控制的Player对象，具体用于水平手势切换播放进度 */
    var player: IMediaPlayer? = null

    /** 单击监听，与[setOnClickListener]任选一个实现监听 */
    var singleTapListener: ((View) -> Unit)? = null

    /** 双击监听 */
    var doubleTapListener: ((View) -> Unit)? = null

    /** 变换监听 */
    var transformListener: ((Matrix) -> Unit)? = null

    //当前的触摸处理器，本类的处理滚动手势与变换手势是互斥的一次完整的手势只会触发其中一种
    private var touchHandler: TouchHandler? = null
        set(value) {
            field = value
            if (touchHandler != null) {
                requestDisallowInterceptTouchEvent(true)
            }
            if (field == TouchHandler.TRANSFORM) {
                removeCallbacks(restoreScreenViewHideTask)
                if (restoreScreenTextView.isGone) {
                    restoreScreenTextView.visible()
                }
            } else {
                if (restoreScreenTextView.isVisible) {
                    removeCallbacks(restoreScreenViewHideTask)
                    postDelayed(restoreScreenViewHideTask, 3 * 1000L)
                }
            }
        }

    //滚动手势处理器，进度、亮度、音量切换，一次手势只会处理其中一种情况
    private var scrollHandler: ScrollHandler? = null

    //本次滚动手势是否处于对外分发嵌套滑动，在手势结束时，需要结束嵌套滑动处理。
    private var inDispatchNestedScrolling = false

    //变换矩阵，记录变换手势累计的变换效果
    private val transformMatrix = Matrix()

    /**
     * 缩放变换
     */
    private val scaleGestureDetector = ScaleGestureDetector(context, object :
        ScaleGestureDetector.OnScaleGestureListener {

        /**
         * 捕捉到缩放手势时，若手势处理者为空或者为变换处理，则确认此次手势为变换手势
         */
        override fun onScaleBegin(detector: ScaleGestureDetector): Boolean {
            detector ?: return false
            if (touchHandler != null && touchHandler != TouchHandler.TRANSFORM) {
                return false
            }
            requestDisallowInterceptTouchEvent(true)
            touchHandler = TouchHandler.TRANSFORM
            postScale(detector)
            return true
        }

        override fun onScale(detector: ScaleGestureDetector): Boolean {
            detector ?: return false
            if (touchHandler != TouchHandler.TRANSFORM) {
                return false
            }
            postScale(detector)
            return true
        }

        override fun onScaleEnd(detector: ScaleGestureDetector) {
            detector ?: return
            if (touchHandler != TouchHandler.TRANSFORM) {
                return
            }
            postScale(detector)
        }

        private fun postScale(detector: ScaleGestureDetector) {
            transformMatrix.postScale(
                detector.scaleFactor,
                detector.scaleFactor,
                detector.focusX,
                detector.focusY
            )
            transformListener?.invoke(transformMatrix)
        }
    })

    /**
     * 位移变换，变换手势的位移处理
     */
    private val translationGestureDetector = GestureDetector(context, object :
        GestureDetector.SimpleOnGestureListener() {

        override fun onDown(e: MotionEvent): Boolean {
            return true
        }

        override fun onScroll(
            e1: MotionEvent?,
            e2: MotionEvent,
            distanceX: Float,
            distanceY: Float
        ): Boolean {
            e1 ?: return true
            e2 ?: return true
            if (touchHandler != TouchHandler.TRANSFORM) {
                return true
            }
            transformMatrix.postTranslate(
                -distanceX,
                -distanceY,
            )
            transformListener?.invoke(transformMatrix)
            return true
        }
    }).apply {
        setIsLongpressEnabled(false)//长按会中断事件，这里不做长按判断
    }

    /**
     * 监听长按事件，若发生长按且当前手势处理者为空，则确认为变换处理，实现长按后可以直接进行位移变换
     */
    private val longPressToTransformGestureDetector = GestureDetector(context, object :
        GestureDetector.SimpleOnGestureListener() {

        override fun onDown(e: MotionEvent): Boolean {
            return true
        }

        override fun onLongPress(e: MotionEvent) {
            super.onLongPress(e)
            if (touchHandler == null) {
                touchHandler = TouchHandler.TRANSFORM
            }
        }
    })


    /**
     * 滚动处理，当无手势处理者，并且滚动发生时为单指，则确认为滚动手势，
     * 依据手势发生位置，与方向，确认滚动处理类型，分发多余的滚动量。
     */
    private val scrollGestureDetector = GestureDetector(context, object :
        GestureDetector.SimpleOnGestureListener() {

        //浮点值矫正，避免一直取整导致数值偏差越来越大，小数怕长记
        private var adjustFloatX = 0f
        private var adjustFloatY = 0f
        private var dx = 0
        private var dy = 0

        override fun onDown(e: MotionEvent): Boolean {
            scrollHandler = null
            adjustFloatX = 0f
            adjustFloatY = 0f
            return true
        }

        override fun onScroll(
            e1: MotionEvent?,
            e2: MotionEvent,
            distanceX: Float,
            distanceY: Float
        ): Boolean {
            e1 ?: return false
            e2 ?: return false
            dx = (distanceX + adjustFloatX).toInt()
            dy = (distanceY + adjustFloatY).toInt()
            adjustFloatX -= (dx - distanceX)
            adjustFloatY -= (dy - distanceY)
            if (touchHandler == null) {
                if (e2.pointerCount == 1) {//滑动时是单指
                    touchHandler = TouchHandler.SCROLL
                }
            }
            if (touchHandler != TouchHandler.SCROLL) {
                return false
            }
            if (!inDispatchNestedScrolling) {
                inDispatchNestedScrolling = true
                nestedScrollingChildHelper.isNestedScrollingEnabled = true
                if (abs(distanceX) <= abs(distanceY)) {//垂直
                    nestedScrollingChildHelper.startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL)
                } else {
                    nestedScrollingChildHelper.startNestedScroll(ViewCompat.SCROLL_AXIS_HORIZONTAL)
                }
            }
            //嵌套滚动 预分发
            val consumed = IntArray(2)
            nestedScrollingChildHelper.dispatchNestedPreScroll(
                dx,
                dy,
                consumed,
                null
            )

            if (scrollHandler == null) {
                if (abs(distanceX) <= abs(distanceY)) {//垂直
                    if (verticalGestureEnable) {
                        if (e1.x <= width / 2) {//亮度调节
                            scrollHandler = BrightnessScrollHandler().apply {
                                init(height)
                            }
                        } else {//音量调节
                            scrollHandler = VolumeScrollHandler().apply {
                                init(height)
                            }
                        }
                    }
                } else {//水平
                    if (horizontalGestureEnable) {
                        //播放进度调节
                        scrollHandler = ProgressScrollHandler().apply {
                            init(width)
                        }
                    }
                }
            }

            //处理滑动
            scrollHandler?.run {
                handleScroll(
                    dx - consumed[0],
                    dy - consumed[1],
                    consumed,
                    e2.action == MotionEvent.ACTION_UP
                            || e2.action == MotionEvent.ACTION_CANCEL
                )
            }

            //嵌套滚动 分发
            nestedScrollingChildHelper.dispatchNestedScroll(
                consumed[0],
                consumed[1],
                dx - consumed[0],
                dy - consumed[1],
                null
            )
            return true
        }

        override fun onFling(
            e1: MotionEvent?,
            e2: MotionEvent,
            velocityX: Float,
            velocityY: Float
        ): Boolean {
            if (touchHandler == null) {
                touchHandler = TouchHandler.SCROLL
            }
            if (touchHandler != TouchHandler.SCROLL) {
                return false
            }
            nestedScrollingChildHelper.dispatchNestedFling(
                velocityX, velocityY,
                nestedScrollingChildHelper.dispatchNestedPreFling(velocityX, velocityY)
            )
            return true
        }

    })

    /**
     * 单击、双击判定，优先级最低，只有在此类没有其他手势处理时才会判断点击
     */
    private val tapGestureDetector = GestureDetector(context, object :
        GestureDetector.SimpleOnGestureListener() {

        override fun onDown(e: MotionEvent): Boolean {
            return true
        }

        override fun onDoubleTap(e: MotionEvent): Boolean {
            if (touchHandler != null) {
                return false
            }
            doubleTapListener?.let {
                it.invoke(this@GestureView)
                return true
            }
            return false
        }

        override fun onSingleTapConfirmed(e: MotionEvent): Boolean {
            performClick()
            if (touchHandler != null) {
                return false
            }
            singleTapListener?.let {
                it.invoke(this@GestureView)
                return true
            }
            return false
        }
    })

    /** 进度、亮度、音量等变化的通知视图 */
    private val hintText = TextView(context).apply {
        setTextColor(Color.WHITE)
        setShadowLayer(10f, 0f, 0f, Color.BLACK)
        textSize = 30f
        gone()
    }
    private val hintShowAnimator = ObjectAnimator.ofFloat(hintText, "alpha", 0.0f, 1.0f)
        .apply {
            addListener(object : Animator.AnimatorListener {
                override fun onAnimationStart(animation: Animator) {
                    hintText.visible()
                }

                override fun onAnimationEnd(animation: Animator) {
                }

                override fun onAnimationCancel(animation: Animator) {
                }

                override fun onAnimationRepeat(animation: Animator) {
                }

            })
        }
    private val hintHideAnimator = ObjectAnimator.ofFloat(hintText, "alpha", 1.0f, 0.0f)
        .apply {
            addListener(object : Animator.AnimatorListener {
                override fun onAnimationStart(animation: Animator) {
                }

                override fun onAnimationEnd(animation: Animator) {
                    hintText.gone()
                }

                override fun onAnimationCancel(animation: Animator) {
                }

                override fun onAnimationRepeat(animation: Animator) {
                }

            })
        }

    /** 场景变化后恢复初始状态的提示视图 */
    private val restoreScreenTextView = TextView(context).apply {
        setBackgroundColor(Color.parseColor("#80000000"))
        setTextColor(Color.WHITE)
        textSize = 30f
        text = context.getString(R.string.player_restore_screen)
        gone()
    }

    /** 恢复初始状态的提示视图的延迟隐藏任务 */
    private val restoreScreenViewHideTask = Runnable { restoreScreenTextView.gone() }

    init {
        addView(hintText, LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT).apply {
            gravity = Gravity.CENTER
        })
        addView(
            restoreScreenTextView,
            LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT).apply {
                gravity = Gravity.CENTER
            })
        restoreScreenTextView.setOnClickListener {
            transformMatrix.reset()
            transformListener?.invoke(transformMatrix)
            this@GestureView.removeCallbacks(restoreScreenViewHideTask)
            it.gone()
        }
    }

    /** 显示提示 */
    fun showHint(hint: String) {
        hintText.text = hint
        if (!hintText.isVisible) {
            hintShowAnimator.start()
        }
    }


    /**
     * 触摸处理
     */
    @SuppressLint("ClickableViewAccessibility")
    override fun onTouchEvent(event: MotionEvent?): Boolean {
        if (!isEnabled) {
            return false
        }
        if (!gestureEnable) {
            return false
        }
        event ?: return false
        //先处理变换
        if (transformGestureEnable && transformListener != null) {
            scaleGestureDetector.onTouchEvent(event)
            translationGestureDetector.onTouchEvent(event)
            longPressToTransformGestureDetector.onTouchEvent(event)
        }
        //再处理滚动，由于触摸事件我要定了，耶稣都没得谈(做人留一线，父控件可以中断事件，我只在确认手势处理者后才请求父类不中断)
        scrollGestureDetector.onTouchEvent(event)
        //最后处理点击
        tapGestureDetector.onTouchEvent(event)
        //最最后对整次手势事件的一个收尾工作
        if (event.action == MotionEvent.ACTION_UP
            || event.action == MotionEvent.ACTION_CANCEL
        ) {
            scrollHandler?.end()
            scrollHandler = null
            touchHandler = null
            if (inDispatchNestedScrolling) {
                inDispatchNestedScrolling = false
                nestedScrollingChildHelper.stopNestedScroll()
            }
        }
        if (hintText.isVisible) {
            hintHideAnimator.start()
        }
        return true
    }

    //region nestedScrollToParent-------------------------------------------------------------------
    private val nestedScrollingChildHelper = NestedScrollingChildHelper(this)

    override fun setNestedScrollingEnabled(enabled: Boolean) {
        super.setNestedScrollingEnabled(enabled)
        nestedScrollingChildHelper.isNestedScrollingEnabled = enabled
    }

    override fun isNestedScrollingEnabled(): Boolean {
        return nestedScrollingChildHelper.isNestedScrollingEnabled
    }

    override fun startNestedScroll(axes: Int): Boolean {
        return nestedScrollingChildHelper.startNestedScroll(axes)
    }

    override fun stopNestedScroll() {
        nestedScrollingChildHelper.stopNestedScroll()
    }

    override fun hasNestedScrollingParent(): Boolean {
        return nestedScrollingChildHelper.hasNestedScrollingParent()
    }

    override fun dispatchNestedPreScroll(
        dx: Int,
        dy: Int,
        consumed: IntArray?,
        offsetInWindow: IntArray?
    ): Boolean {
        return nestedScrollingChildHelper.dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow)
    }

    override fun dispatchNestedScroll(
        dxConsumed: Int,
        dyConsumed: Int,
        dxUnconsumed: Int,
        dyUnconsumed: Int,
        offsetInWindow: IntArray?
    ): Boolean {
        return nestedScrollingChildHelper.dispatchNestedScroll(
            dxConsumed,
            dyConsumed,
            dxUnconsumed,
            dyUnconsumed,
            offsetInWindow
        )
    }

    override fun dispatchNestedPreFling(velocityX: Float, velocityY: Float): Boolean {
        return nestedScrollingChildHelper.dispatchNestedPreFling(velocityX, velocityY)
    }

    override fun dispatchNestedFling(
        velocityX: Float,
        velocityY: Float,
        consumed: Boolean
    ): Boolean {
        return nestedScrollingChildHelper.dispatchNestedFling(velocityX, velocityY, consumed)
    }

    //endregion

    //region scrollHandler--------------------------------------------------------------------------

    private abstract inner class ScrollHandler {

        var length: Int = 0

        open fun init(length: Int) {
            this.length = length
        }

        abstract fun handleScroll(dx: Int, dy: Int, consumed: IntArray, scrollEnd: Boolean)

        open fun end() {}
    }

    private inner class BrightnessScrollHandler : ScrollHandler() {
        private var max = 0
        private var progress = 0

        private var pixelProgress = 0f

        override fun init(length: Int) {
            super.init(length)
            max = 100
            progress = BrightnessManager.brightness.value ?: 0

            pixelProgress = length * (progress * 1f / max)
        }

        override fun handleScroll(dx: Int, dy: Int, consumed: IntArray, scrollEnd: Boolean) {
            if (dy > 0) {//亮度增强
                min(dy, (length - pixelProgress + 0.5).toInt()).run {
                    consumed[1] = consumed[1] + this
                    pixelProgress += this
                    updateProgress((pixelProgress / length * max).toInt())
                }
            } else if (dy < 0) {//亮度减弱
                max(dy, (-pixelProgress).toInt()).run {
                    consumed[1] = consumed[1] + this
                    pixelProgress += this
                    updateProgress((pixelProgress / length * max).toInt())
                }
            }
        }

        private fun updateProgress(newProgress: Int) {
            if (this.progress == newProgress) {
                return
            }
            progress = newProgress
            BrightnessManager.brightness.value = progress
            showHint(context.getString(R.string.player_brightness, progress * 100 / max))
        }

    }

    private inner class VolumeScrollHandler : ScrollHandler() {
        private var max = 0
        private var progress = 0

        private var pixelProgress = 0f

        override fun init(length: Int) {
            super.init(length)
            max = MediaVolumeManager.deviceMaxVolume
            progress = MediaVolumeManager.volume.value ?: 0

            pixelProgress = length * (progress * 1f / max)
        }

        override fun handleScroll(dx: Int, dy: Int, consumed: IntArray, scrollEnd: Boolean) {
            if (dy > 0) {//音量增强
                min(dy, (length - pixelProgress + 0.5).toInt()).run {
                    consumed[1] = consumed[1] + this
                    pixelProgress += this
                    updateProgress((pixelProgress / length * max).toInt())
                }
            } else if (dy < 0) {//音量减弱
                max(dy, (-pixelProgress).toInt()).run {
                    consumed[1] = consumed[1] + this
                    pixelProgress += this
                    updateProgress((pixelProgress / length * max).toInt())
                }
            }
        }

        private fun updateProgress(newProgress: Int) {
            if (this.progress == newProgress) {
                return
            }
            progress = newProgress
            MediaVolumeManager.volume.value = progress
            showHint(context.getString(R.string.player_volume, progress * 100 / max))
        }

    }

    private inner class ProgressScrollHandler : ScrollHandler() {
        private var lengthStep = 6 * 60 * 1000L
        private var pixelStep = lengthStep / 1000

        private var progress: Long = 0L
        private var duration: Long = 0L

        override fun init(length: Int) {
            super.init(length)
            progress = player?.getContentProgress() ?: 0L
            duration = player?.getContentDuration() ?: 0L

            lengthStep = min(6 * 60 * 1000L, duration * 2)
            if (lengthStep < length) {
                lengthStep = length.toLong()
            }
            pixelStep = lengthStep / length
        }

        override fun handleScroll(dx: Int, dy: Int, consumed: IntArray, scrollEnd: Boolean) {
            if (duration == 0L) {
                return
            }
            if (dx > 0) {//后退
                min(dx, ((progress) / pixelStep).toInt()).run {
                    consumed[0] = consumed[0] + this
                    progress -= this * pixelStep
                    updateHint()
                }
            } else if (dx < 0) {//前进
                max(dx, ((progress - duration) / pixelStep).toInt()).run {
                    consumed[0] = consumed[0] + this
                    progress -= this * pixelStep
                    updateHint()
                }
            }
            if (scrollEnd) {
                player?.setContentProgress(progress)
            }
        }


        private var tempSecond = 0
        private fun updateHint() {
            tempSecond = (progress / 1000).toInt()
            if (duration >= 60 * 60 * 1000L) {
                showHint(
                    String.format(
                        "%02d:%02d:%02d",
                        tempSecond / 60 / 60,
                        (tempSecond % (60 * 60)) / 60,
                        tempSecond % 60,
                    )
                )
            } else {
                showHint(
                    String.format(
                        "%02d:%02d",
                        tempSecond / 60,
                        tempSecond % 60,
                    )
                )
            }
        }

        override fun end() {
            super.end()
            player?.setContentProgress(progress)
        }

    }

    //endregion
}